//package org.kjob.common.utils;
///*
//Copyright [2020] [PowerJob]
//
//Licensed under the Apache License, Version 2.0 (the "License");
//you may not use this file except in compliance with the License.
//You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
//Unless required by applicable law or agreed to in writing, software
//distributed under the License is distributed on an "AS IS" BASIS,
//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//See the License for the specific language governing permissions and
//limitations under the License.
// */
//
//import lombok.extern.slf4j.Slf4j;
//import org.apache.commons.lang3.StringUtils;
//import org.apache.commons.lang3.tuple.Pair;
//
//
//import java.io.IOException;
//import java.net.*;
//import java.util.*;
//import java.util.concurrent.ThreadLocalRandom;
//import java.util.regex.Pattern;
//
//import static java.util.Collections.emptyList;
//
///**
// * IP and Port Helper for RPC
// *
// * @author from dubbo, optimize by tjq
// * @since 2020/8/8
// */
//@Slf4j
//public class NetUtils {
//
//    /**
//     * returned port range is [30000, 39999]
//     */
//    private static final int RND_PORT_START = 30000;
//    private static final int RND_PORT_END = 65535;
//
//    private static volatile String HOST_ADDRESS;
//    private static final String LOCALHOST_VALUE = "127.0.0.1";
//    private static volatile InetAddress LOCAL_ADDRESS = null;
//    private static final Pattern IP_PATTERN = Pattern.compile("\\d{1,3}(\\.\\d{1,3}){3,5}$");
//    private static final String ANY_HOST_VALUE = "0.0.0.0";
//
//    private NetUtils() {
//    }
//
//    public static int getRandomPort() {
//        return ThreadLocalRandom.current().nextInt(RND_PORT_START, RND_PORT_END);
//    }
//
//    /**
//     * 检测某个 IP 端口是否可用
//     * @param ip IP
//     * @param port 端口
//     * @return 是否可用
//     */
//    public static boolean checkIpPortAvailable(String ip, int port) {
//        try (Socket socket = new Socket()) {
//            socket.connect(new InetSocketAddress(ip, port), 1000);
//            return true;
//        } catch (Exception e) {
//            return false;
//        }
//    }
//
//    /**
//     * 获取本机 IP 地址
//     *
//     * @return 本机 IP 地址
//     */
//    public static String getLocalHost() {
//        return getLocalHostWithNetworkInterfaceChecker(null);
//    }
//
//    public static String getLocalHostWithNetworkInterfaceChecker(NetworkInterfaceChecker networkInterfaceChecker) {
//        if (HOST_ADDRESS != null) {
//            return HOST_ADDRESS;
//        }
//
//        String addressFromJVM = System.getProperty(PowerJobDKey.BIND_LOCAL_ADDRESS);
//        if (StringUtils.isNotEmpty(addressFromJVM)) {
//            log.info("[Net] use address from[{}]: {}", PowerJobDKey.BIND_LOCAL_ADDRESS, addressFromJVM);
//            return HOST_ADDRESS = addressFromJVM;
//        }
//
//        InetAddress address = getLocalAddress(networkInterfaceChecker);
//        if (address != null) {
//            return HOST_ADDRESS = address.getHostAddress();
//        }
//        return LOCALHOST_VALUE;
//    }
//
//    /**
//     * 隔离调用 scope，核心场景才能直接调用 getLocalHost，方便查看使用点
//     * @return IP
//     */
//    public static String getLocalHost4Test() {
//        return getLocalHost();
//    }
//
//    /**
//     * Find first valid IP from local network card
//     *
//     * @return first valid local IP
//     */
//    public static InetAddress getLocalAddress(NetworkInterfaceChecker networkInterfaceChecker) {
//        if (LOCAL_ADDRESS != null) {
//            return LOCAL_ADDRESS;
//        }
//        InetAddress localAddress = getLocalAddress0(networkInterfaceChecker);
//        LOCAL_ADDRESS = localAddress;
//        return localAddress;
//    }
//
//    private static InetAddress getLocalAddress0(NetworkInterfaceChecker networkInterfaceChecker) {
//        // @since 2.7.6, choose the {@link NetworkInterface} first
//        try {
//            InetAddress addressOp = getFirstReachableInetAddress( findNetworkInterface(networkInterfaceChecker));
//            if (addressOp != null) {
//                return addressOp;
//            }
//        } catch (Throwable e) {
//            log.warn("[Net] getLocalAddress0 failed.", e);
//        }
//
//        InetAddress localAddress = null;
//        try {
//            localAddress = InetAddress.getLocalHost();
//            Optional<InetAddress> addressOp = toValidAddress(localAddress);
//            if (addressOp.isPresent()) {
//                return addressOp.get();
//            }
//        } catch (Throwable e) {
//            log.warn("[Net] getLocalAddress0 failed.", e);
//        }
//
//
//        return localAddress;
//    }
//
//    private static InetAddress getFirstReachableInetAddress(NetworkInterface networkInterface) {
//
//        if(networkInterface == null ){
//            return null;
//        }
//        Enumeration<InetAddress> addresses = networkInterface.getInetAddresses();
//        while (addresses.hasMoreElements()) {
//            Optional<InetAddress> addressOp = toValidAddress(addresses.nextElement());
//            if (addressOp.isPresent()) {
//                try {
//                    if (addressOp.get().isReachable(100)) {
//                        return addressOp.get();
//                    }
//                } catch (IOException e) {
//                    // ignore
//                }
//            }
//        }
//        return null;
//    }
//
//    /**
//     * Get the suitable {@link NetworkInterface}
//     *
//     * @return If no {@link NetworkInterface} is available , return <code>null</code>
//     * @since 2.7.6
//     */
//    public static NetworkInterface findNetworkInterface(NetworkInterfaceChecker networkInterfaceChecker) {
//
//        List<NetworkInterface> validNetworkInterfaces = emptyList();
//        try {
//            validNetworkInterfaces = getValidNetworkInterfaces();
//        } catch (Throwable e) {
//            log.warn("[Net] findNetworkInterface failed", e);
//        }
//
//        // sort by interface index, the smaller is preferred.
//         validNetworkInterfaces.sort(Comparator.comparingInt(NetworkInterface::getIndex));
//
//        // Try to find the preferred one
//        for (NetworkInterface networkInterface : validNetworkInterfaces) {
//            if (isPreferredNetworkInterface(networkInterface)) {
//                log.info("[Net] use preferred network interface: {}", networkInterface);
//                return networkInterface;
//            }
//            if (isPassedCheckNetworkInterface(networkInterface, networkInterfaceChecker)) {
//                log.info("[Net] use PassedCheckNetworkInterface: {}", networkInterface);
//                return networkInterface;
//            }
//        }
//        // If not found, try to get the first one
//        for (NetworkInterface networkInterface : validNetworkInterfaces) {
//            InetAddress addressOp = getFirstReachableInetAddress(networkInterface);
//            if (addressOp != null) {
//                return networkInterface;
//            }
//        }
//
//        return first(validNetworkInterfaces);
//    }
//
//    /**
//     * 通过用户方法判断是否为目标网卡
//     * @param networkInterface networkInterface
//     * @param networkInterfaceChecker 判断方法
//     * @return true or false
//     */
//    static boolean isPassedCheckNetworkInterface(NetworkInterface networkInterface, NetworkInterfaceChecker networkInterfaceChecker) {
//        if (networkInterfaceChecker == null) {
//            return false;
//        }
//        log.info("[Net] try to choose NetworkInterface by NetworkInterfaceChecker, current NetworkInterface: {}", networkInterface);
//        try {
//            return networkInterfaceChecker.ok(networkInterface, getFirstReachableInetAddress(networkInterface));
//        } catch (Exception e) {
//            log.warn("[Net] isPassedCheckerNetworkInterface failed, current networkInterface: {}", networkInterface, e);
//        }
//        return false;
//    }
//
//    private static Optional<InetAddress> toValidAddress(InetAddress address) {
//        if (address instanceof Inet6Address) {
//            Inet6Address v6Address = (Inet6Address) address;
//            if (isPreferIPV6Address()) {
//                return Optional.ofNullable(normalizeV6Address(v6Address));
//            }
//        }
//        if (isValidV4Address(address)) {
//            return Optional.of(address);
//        }
//        return Optional.empty();
//    }
//
//    /**
//     * Check if an ipv6 address
//     *
//     * @return true if it is reachable
//     */
//    static boolean isPreferIPV6Address() {
//        return Boolean.getBoolean("java.net.preferIPv6Addresses");
//    }
//
//    static boolean isValidV4Address(InetAddress address) {
//        if (address == null || address.isLoopbackAddress()) {
//            return false;
//        }
//
//        String name = address.getHostAddress();
//        return (name != null
//                && IP_PATTERN.matcher(name).matches()
//                && !ANY_HOST_VALUE.equals(name)
//                && !LOCALHOST_VALUE.equals(name));
//    }
//
//    /**
//     * normalize the ipv6 Address, convert scope name to scope id.
//     * e.g.
//     * convert
//     * fe80:0:0:0:894:aeec:f37d:23e1%en0
//     * to
//     * fe80:0:0:0:894:aeec:f37d:23e1%5
//     * <p>
//     * The %5 after ipv6 address is called scope id.
//     * see java doc of {@link Inet6Address} for more details.
//     *
//     * @param address the input address
//     * @return the normalized address, with scope id converted to int
//     */
//    static InetAddress normalizeV6Address(Inet6Address address) {
//        String addr = address.getHostAddress();
//        int i = addr.lastIndexOf('%');
//        if (i > 0) {
//            try {
//                return InetAddress.getByName(addr.substring(0, i) + '%' + address.getScopeId());
//            } catch (UnknownHostException e) {
//                // ignore
//                log.debug("Unknown IPV6 address: ", e);
//            }
//        }
//        return address;
//    }
//
//    /**
//     * Get the valid {@link NetworkInterface network interfaces}
//     *
//     * @return non-null
//     * @throws SocketException SocketException if an I/O error occurs.
//     * @since 2.7.6
//     */
//    private static List<NetworkInterface> getValidNetworkInterfaces() throws SocketException {
//        List<NetworkInterface> validNetworkInterfaces = new LinkedList<>();
//        Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
//        while (interfaces.hasMoreElements()) {
//            NetworkInterface networkInterface = interfaces.nextElement();
//            // ignore
//            if (ignoreNetworkInterface(networkInterface)) {
//                continue;
//            }
//            // 根据用户 -D 参数忽略网卡
//            if (ignoreInterfaceByConfig(networkInterface.getDisplayName()) || ignoreInterfaceByConfig(networkInterface.getName())) {
//                continue;
//            }
//            validNetworkInterfaces.add(networkInterface);
//        }
//        return validNetworkInterfaces;
//    }
//
//    /**
//     * @param networkInterface {@link NetworkInterface}
//     * @return if the specified {@link NetworkInterface} should be ignored, return <code>true</code>
//     * @throws SocketException SocketException if an I/O error occurs.
//     * @since 2.7.6
//     */
//    private static boolean ignoreNetworkInterface(NetworkInterface networkInterface) throws SocketException {
//        return networkInterface == null
//                || networkInterface.isLoopback()
//                || networkInterface.isVirtual()
//                || !networkInterface.isUp();
//    }
//
//    /**
//     * Take the first element from the specified collection
//     *
//     * @param values the collection object
//     * @param <T>    the type of element of collection
//     * @return if found, return the first one, or <code>null</code>
//     * @since 2.7.6
//     */
//    public static <T> T first(Collection<T> values) {
//        if (values == null || values.isEmpty()) {
//            return null;
//        }
//        if (values instanceof List) {
//            List<T> list = (List<T>) values;
//            return list.get(0);
//        } else {
//            return values.iterator().next();
//        }
//    }
//
//    /**
//     * Is preferred {@link NetworkInterface} or not
//     *
//     * @param networkInterface {@link NetworkInterface}
//     * @return if the name of the specified {@link NetworkInterface} matches
//     * the property value from {@link PowerJobDKey#PREFERRED_NETWORK_INTERFACE}, return <code>true</code>,
//     * or <code>false</code>
//     */
//    public static boolean isPreferredNetworkInterface(NetworkInterface networkInterface) {
//        String preferredNetworkInterface = System.getProperty(PowerJobDKey.PREFERRED_NETWORK_INTERFACE);
//        if (Objects.equals(networkInterface.getDisplayName(), preferredNetworkInterface)) {
//            return true;
//        }
//        // 兼容直接使用网卡名称的情况，比如 Realtek PCIe GBE Family Controller
//        return Objects.equals(networkInterface.getName(), preferredNetworkInterface);
//    }
//
//    public static Pair<String, Integer> splitAddress2IpAndPort(String address) {
//        String[] split = address.split(":");
//        return Pair.of(split[0], Integer.valueOf(split[1]));
//    }
//
//    static boolean ignoreInterfaceByConfig(String interfaceName) {
//        String regex = System.getProperty(PowerJobDKey.IGNORED_NETWORK_INTERFACE_REGEX);
//        if (StringUtils.isBlank(regex)) {
//            return false;
//        }
//        if (interfaceName.matches(regex)) {
//            log.info("[Net] ignore network interface: {} by regex({})", interfaceName, regex);
//            return true;
//        }
//        return false;
//    }
//
//    @FunctionalInterface
//    public interface NetworkInterfaceChecker {
//        boolean ok(NetworkInterface networkInterface, InetAddress inetAddress);
//    }
//}
